from fastapi import FastAPI, HTTPException,Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse

from pydantic import BaseModel
from turingmachine import TuringMachine, parse_instructions
from filelite import (
    load_machine_by_id, load_machines, load_machines_without_raw_instructions,save_machine, 
    delete_machine, init_db
)
import asyncio
import time

app = FastAPI()

from starlette.middleware.base import BaseHTTPMiddleware
import logging


# 配置日志
logger = logging.getLogger("uvicorn")

class ExecutionTimeMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        start_time = time.time()
        response = await call_next(request)
        end_time = time.time()
        duration = (end_time - start_time) * 1000  # 转换为 ms
        logger.info(f"Request to {request.url.path} took {duration:.2f}ms")
        return response

# 添加中间件
app.add_middleware(ExecutionTimeMiddleware)


# 初始化数据库
init_db()

# 全局锁
global_lock = asyncio.Lock()

class MachineDescription(BaseModel):
    instructions: str
    tape: str
    state: str
    head_position: int = 0
    cycles: int = 0  # 添加 cycles 属性，默认值为 0
    blank_symbol: str = " "  # 新增 blank_symbol 字段，默认为空格

class StepResponse(BaseModel):
    current_state: str
    tape: str
    head_position: int

app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/", response_class=HTMLResponse)
async def read_index():
    with open("static/index.html", encoding='utf-8') as f:
        return HTMLResponse(content=f.read())

@app.get("/machines", response_model=dict)
async def get_all_machines():
    async with global_lock:
        machines = load_machines_without_raw_instructions()  # 使用不包含 raw_instructions 的函数
        return machines

@app.get("/machine/{machine_id}", response_model=dict)
async def get_machine(machine_id: str):
    async with global_lock:
        machine = load_machine_by_id(machine_id)
        if not machine:
            raise HTTPException(status_code=404, detail="Machine not found")
        return machine

@app.post("/create_machine", response_model=dict)
async def create_machine(description: MachineDescription):
    async with global_lock:
        instructions, parsed_config = parse_instructions(description.instructions)

        # 如果 description 中的 state 或 blank_symbol 为空字符串，则使用 parsed_config 中的值
        state = description.state if description.state else parsed_config.get("state", "")
        blank_symbol = description.blank_symbol if description.blank_symbol else parsed_config.get("blank_symbol", " ")

        machine_id = save_machine(machine={
            "raw_instructions": description.instructions,
            "tape": description.tape,
            "state": state,
            "blank_symbol": blank_symbol,  # 保存 blank_symbol 字段
            "head_position": description.head_position,
            "cycles": description.cycles  # 保存 cycles 值
        })
        return {"id": machine_id}

@app.post("/update_machine/{machine_id}", response_model=dict)
async def update_machine(machine_id: str, description: MachineDescription):
    async with global_lock:
        instructions, parsed_config = parse_instructions(description.instructions)

        # 如果 description 中的 state 或 blank_symbol 为空字符串，则使用 parsed_config 中的值
        state = description.state if description.state else parsed_config.get("state", "")
        blank_symbol = description.blank_symbol if description.blank_symbol else parsed_config.get("blank_symbol", " ")

        save_machine(machine_id, {
            "raw_instructions": description.instructions,
            "tape": description.tape,
            "state": state,
            "blank_symbol": blank_symbol,  # 更新 blank_symbol 字段
            "head_position": description.head_position,
            "cycles": description.cycles  # 更新 cycles 值
        })
        return {"OK": "yes"}

@app.post("/copy_machine/{machine_id}", response_model=dict)
async def copy_machine(machine_id: str):
    async with global_lock:
        # Load the original machine
        machine = load_machine_by_id(machine_id)
        if not machine:
            raise HTTPException(status_code=404, detail="Machine not found")

        # Create a new machine with the same properties
        new_machine = {
            "raw_instructions": machine["raw_instructions"],
            "tape": machine["tape"],
            "state": machine["state"],
            "blank_symbol": machine["blank_symbol"],
            "head_position": machine["head_position"],
            "cycles": machine["cycles"]
        }

        # Save the new machine and return its ID
        new_machine_id = save_machine(machine=new_machine)
        new_machine['id']= new_machine_id
        return new_machine

@app.post("/delete_machine/{machine_id}", response_model=dict)
async def delete_machine_api(machine_id: str):
    async with global_lock:
        if not delete_machine(machine_id):
            raise HTTPException(status_code=404, detail="Machine not found")
        return {"detail": f"Machine {machine_id} deleted successfully"}

async def step_all_machines():
    while True:
        async with global_lock:
            start_time = time.time()

            machines = load_machines()
            for machine_id, machine in machines.items():
                m = TuringMachine(machine)
                m.step()
                save_machine(machine_id, m.desc())

            elapsed = time.time() - start_time
            print(f"Step elapsed time: {elapsed:.2f} seconds")  # 打印本轮耗时

            sleep_time = max(1 - elapsed, 0)  # 确保不会出现负的睡眠时间
        await asyncio.sleep(sleep_time)

@app.on_event("startup")
async def start_background_task():
    asyncio.create_task(step_all_machines())

# Run FastAPI server with:
# uvicorn apiserver:app --reload --host 0.0.0.0 --port 8085
